

namespace Stee.CAP8.MessageBus.Kafka.Models
{
    using System;
    using System.Collections.Generic;
    using NLog;
    using Stee.CAP8.MessageBus.Interfaces.Delegates;
    using Stee.CAP8.MessageBus.Interfaces.Models;
    using Stee.CAP8.MessageBus.Kafka.Utility;

    /// <summary>
    /// Handler for command message type
    /// Multiple entities can use the same "update" action, therefore topic is required to identify the action.
    /// </summary>
    internal sealed class HandlerCommandDetails : HandlerDetails<CommandMessage>
    {
        private readonly static Logger Logger = LogManager.GetCurrentClassLogger();


        #region Fields

        /// <summary>
        /// Overridden logger
        /// </summary>
        protected override Logger NLogger => Logger;

        /// <summary>
        /// Topic to message handler mapping
        /// </summary>
        private readonly Dictionary<string, HandleSubscribe<CommandMessage>> topicActionHandlers;

        #endregion Fields


        #region Constructor

        /// <summary>
        /// Initializes a new instance of the HandlerCommandDetails class
        /// </summary>
        internal HandlerCommandDetails()
        {
            this.topicActionHandlers = new Dictionary<string, HandleSubscribe<CommandMessage>>();
        }

        #endregion Constructor


        #region Public Methods

        /// <summary>
        /// Add new message handler based on specified action name
        /// </summary>
        /// <param name="handler">Message handler</param>
        /// <param name="topicName">Topic name</param>
        /// <param name="actionName">Action name</param>
        /// <returns>Return TRUE if added successfully else FALSE</returns>
        internal bool AddActionHandler(HandleSubscribe<CommandMessage> handler, string topicName, string actionName)
        {
            try
            {
                if (handler == null)
                {
                    throw new ArgumentNullException($"Null action handler encountered. {nameof(handler)}");
                }

                var topic = ValidateHelper.CheckString(topicName);
                var action = ValidateHelper.CheckString(actionName);
                var topicAction = GenerateTopicActionName(topic, action);

                if (this.topicActionHandlers.ContainsKey(topicAction))
                {
                    this.topicActionHandlers[topicAction] += handler;
                }
                else
                {
                    this.topicActionHandlers[topicAction] = handler;
                }

                return true;
            }
            catch (Exception ex)
            {
                NLogger.Error(ex, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Remove existing message handler based on specified action name
        /// </summary>
        /// <param name="handler">Message handler</param>
        /// <param name="topicName">Topic name</param>
        /// <param name="actionName">Action name</param>
        /// <returns>Return TRUE if removed successfully else FALSE</returns>
        internal bool RemoveActionHandler(HandleSubscribe<CommandMessage> handler, string topicName, string actionName)
        {
            try
            {
                if (handler == null)
                {
                    throw new ArgumentNullException($"Null action handler encountered. {nameof(handler)}");
                }

                var topic = ValidateHelper.CheckString(topicName);
                var action = ValidateHelper.CheckString(actionName);
                var topicAction = GenerateTopicActionName(topic, action);

                if (this.topicActionHandlers.ContainsKey(topicAction))
                {
                    this.topicActionHandlers[topicAction] -= handler;
                    return true;
                }
                else
                {
                    //// Action name not found in dictionary
                    NLogger.Warn($"Failed to remove action handler, topic action name does not exist. Topic action: {topicAction}");
                    return false;
                }
            }
            catch (Exception ex)
            {
                NLogger.Error(ex, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Get existing message handler based on specified action name
        /// </summary>
        /// <param name="topicName">Topic name</param>
        /// <param name="actionName">Action name</param>
        /// <returns>Return action message handler</returns>
        internal HandleSubscribe<CommandMessage> GetActionHandler(string topicName, string actionName)
        {
            try
            {
                var topic = ValidateHelper.CheckString(topicName);
                var action = ValidateHelper.CheckString(actionName);
                var topicAction = GenerateTopicActionName(topic, action);

                if (this.topicActionHandlers.ContainsKey(topicAction))
                {
                    return this.topicActionHandlers[topicAction];
                }
                else
                {
                    //// Action name not found in dictionary
                    NLogger.Warn($"Failed to remove action handler, topic action name does not exist. Topic action: {actionName}");
                    return null;
                }
            }
            catch (Exception ex)
            {
                NLogger.Error(ex, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Invoke message handling
        /// </summary>
        /// <param name="topicName">Message topic name</param>
        /// <param name="message">Command message</param>
        /// <param name="publishUtcDT">Message published date time in UTC</param>
        /// <returns>Return TRUE if invoked successfully else FALSE</returns>
        internal override bool Invoke(TopicName topicName, CommandMessage message, DateTime publishUtcDT)
        {
            try
            {
                if (topicName == null)
                {
                    throw new ArgumentNullException($"Failed to invoke event message handling, parameter \"{nameof(topicName)}\" is null.");
                }
                else if (message == null)
                {
                    throw new ArgumentNullException($"Failed to invoke event message handling, parameter \"{nameof(message)}\" is null.");
                }

                //// Topic
                var topic = topicName.Name;

                if (this.Handlers.ContainsKey(topic))
                {
                    var topicHandlers = this.Handlers[topic];
                    topicHandlers?.Invoke(topicName, message, publishUtcDT);
                }

                //// Action
                var action = ValidateHelper.CheckString(message.Action);
                var topicAction = GenerateTopicActionName(topic, action);

                if (this.topicActionHandlers.ContainsKey(topicAction))
                {
                    var actionHandlers = this.topicActionHandlers[topicAction];
                    actionHandlers?.Invoke(topicName, message, publishUtcDT);
                }

                return true;
            }
            catch (Exception ex)
            {
                NLogger.Error(ex, ex.Message);
                return false;
            }
        }

        #endregion Public Methods
    }
}
